Reflux 算是比较新的东西,由于自己水平有限,刚接触,不能很好地去使用 Reflux 来处理数据,下面是我使用 Reflux 逐步进化的过程(当然最终状态不一定就是标准的 Reflux 使用方式):
第一步:初识 Reflux
一直在听人说 Reflux ,说这个东西比较适合中小型的前端项目,使用起来很方便,于是我就找到了 Reflux 在 GitHub 的主页。
文档说 dispatcher 被移除了,没关系,反正我也没用过 Flux 。
于是继续阅读关于 actions 和 stores 的文档。由于心浮气躁急着用,看文档很马虎,action 、 store 可以监听过去监听过来的,还有 store 可以 connect 啥的,完全看晕了,无法用 Reflux 组织起一个完整的处理流程。但是没关系,我就按照文档上的这些 listen 啥的,自己来写写看吧。
于是, 创建 action ,在 store 中用 listenTo 来监听 action ,然后请求数据,store trigger 返回数据。写的时候,由于完全不理解 Reflux 怎么用,一通胡乱监听,写出来的代码不三不四,看着都觉得累。为了照顾项目进度,放弃 Reflux ,自己写一个 service 层吧。
第二步:认识了一点 Reflux
过了几天,对 Reflux 心有不甘,于是转头再去看 Reflux 文档,同时也很开心找到一篇使用 Reflux 的经验文章,于是知道了 action 可以当成方法调用,在 action 中监听调用,发出请求之类的,然后 store 做一些存储等操作,再 trigger ,component 中通过 mixin 来监听 store 中的 trigger ,然后做一些界面变动,摘录一段那篇文章中的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| var Reflux = require('reflux'); var React = require('react'); var UserAction = Reflux.createAction({ 'login': {children: ['success', 'failed']} }); UsersAction.login.listen(function(data) { $.post('/api/users/Action/login', data).then(this.success, this.failed); }); var UserStore = Reflux.createStore({ listenables: UserAction, onLoginSuccess: function(payload) { this.trigger(payload); }, onLoginFailed: function(payload) { this.trigger(payload); } }); var UserComponent = React.createClass({ mixins: [Reflux.connect(UserStore, 'user')], render: function() { return <span>{this.state.user.name}</span>; } });
|
感觉自己似乎知道怎么来组织流程了,于是很开心地又去改造代码,希望能用上 Reflux 。
写了一会儿,发现完了,因为有这样的场景:就拿上述一小段代码来说,UserAction 中很可能还有其它 action ,例如:
1 2 3 4
| var UserAction = Reflux.createActions({ 'login': {children: ['success', 'failed']}, 'register': {children: ['success', 'failed']} });
|
login 和 register 两个 action 都会触发 UserStore 中相应方法的调用,然后这些方法再调用 trigger ,然后改变 UserComponent 中 state.user
的值,此处有两个问题:
- 1、登录和注册最终得到的数据真的都要反映到 UserComponent 的
state.user
上吗?这样合适吗?
- 2、如果登录报错了,怎么通知 UserComponent ,怎么告诉其错误信息?
想了想,有种方案:组织好 trigger 返回的数据结构,比如像这样:
1 2 3 4 5
| { actionType: 'login', status: 0, message: 'an error occurred' }
|
但是转念一想,这明显不对,肯定不是标准的用法,这样的话我又得在 component 中写好多代码来分析这些分发复杂的情况,太不优雅了。
想了半天,实在没想出好的方式,在《聊一聊基于Flux的前端系统》中也没找到相关内容。
于是,使用 Reflux 的想法再次被搁置,继续使用 service 吧!
第三步:别扭的方式解决出错处理
改回 service 之后,心中还是蛮不爽的,便去一个牛人云集的 React 群(161461760)求助,初步描述完我的问题之后,群中一位热心网友提出了他的方式:给 store 添加方法,获取 action 执行的结果。
感觉这种方式似乎能解决问题,虽然还是有点别扭,于是代码变成了这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| var Reflux = require('reflux'); var React = require('react'); var UserAction = Reflux.createActions({ 'login': {children: ['success', 'failed']} }); UsersAction.login.listen(function(data) { $.post('/api/users/Action/login', data).then(this.success, this.failed); }); var userStoreMixin = { getLoginResult() { if (this._error) { throw this._error; } return this._user; } }; var UserStore = Reflux.createStore({ listenables: UserAction, mixins: [userStoreMixin], onLoginSuccess(payload) { this._error = null; this.trigger(payload); }, onLoginFailed(error) { if (error.status === -1) { this._error = new UnloginError(error.message); } else { this._error = new Error(error.message); } this.trigger(); } }); var UserComponent = React.createClass({ mixins: [Reflux.listenTo(UserStore, 'onUserStore')], onUserStore() { try { this.setState({ user: UserStore.getLoginResult() }); } catch (e) { if (e instanceof UnloginError) { alert('not login'); } else { alert(e.message); } } }, render() { return <span>{this.state.user.name}</span>; } });
|
似乎还行,于是开开心心地翻新项目代码,将 service 改成“这种的 Reflux ”。
第四步:产生新的想法
按照第三步的思维使用了一段时间之后,感觉实在是别扭,越来越感受到这不是标准的方案,写出来的代码看着有点丑。
于是想啊想,突然,灵光一闪,还是应该回归到第二步中写的那个例子啊,store 应该只是用来处理正确的数据
,至于那些报错什么的,可以用额外的 action 、 store 来处理啊!于是上述代码应该是这个样子的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| var Reflux = require('reflux'); var React = require('react'); var UserAction = Reflux.createActions({ 'login': {children: ['success', 'failed']} }); UsersAction.login.listen(function(data) { $.post('/api/users/Action/login', data).then(this.success, this.failed); }); var ErrorAction = Reflux.createActions({ Unlogin: {}, error: {} }); var UserStore = Reflux.createStore({ listenables: UserAction, onLoginSuccess(payload) { this.trigger(payload); }, onLoginFailed(payload) { if (error.status === -1) { ErrorAction.Unlogin(error.message); } else { ErrorAction.error(error.message); } } }); var ErrorStoreMixin = { UNLOGIN: 1, ERROR: 2 }; var ErrorStore = Reflux.createStore({ listenables: ErrorAction, mixins: [ErrorStoreMixin], onUnlogin(message) { this.trigger({type: this.UNLOGIN, message: message}); }, onError(message) { this.trigger({type: this.ERROR, message: message}); } }); var UserComponent = React.createClass({ mixins: [Reflux.connect(UserStore, 'user'), Reflux.listenTo(ErrorStore, 'onErrorStore')], onErrorStore(error) { if (error.type === ErrorStore.UNLOGIN) { alert('not login'); } else if (error.type === ErrorStore.ERROR) { alert(error.message); } }, render() { return <span>{this.state.user.name}</span>; } });
|
现在,感觉似乎完美一点了,代码看着也相对优雅。
不过,到目前为止,还有一点疑问:按照这种 store 写法,似乎会创建很多 store ,是否需要控制 store 数量,如果有必要,如何整合各个 store ?
带着一些疑问,继续前行吧,骚年!
(后续有使用心得的时候会继续更新本文章)